package dropbox;

import api.API;
import api.Message;
import api.User;
import error.ConfigFileNotFoundException;
import error.FileCorruptedException;
import error.NotLoggedInException;
import error.UserAlreadyExistsException;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;

public class Dropbox implements API {

    private FileIO io;
    private User user;
    private Config config;
    private Map<String, List<Message>> allMessages;

    public Dropbox() throws IOException, ConfigFileNotFoundException, FileCorruptedException {
        io = new FileIO();
    }

    @Override
    public boolean userExists(String username) throws IOException {
        return this.io.userFileExists(username);
    }

    @Override
    public void newUser(String username) throws IOException, UserAlreadyExistsException {
        if (!this.io.userFileExists(username)) {
            this.io.save(new User(username));
        } else {
            throw new UserAlreadyExistsException();
        }
    }

    @Override
    public boolean isLoggedIn() {
        return (this.user != null);
    }

    @Override
    public void login(String username) throws IOException, FileCorruptedException {
        this.user = this.io.load(username);
        this.loadAllUserMessages();
    }

    @Override
    public String getUsername() throws NotLoggedInException {
        if (this.isLoggedIn()) {
            return this.user.getUsername();
        }
        throw new NotLoggedInException();
    }

    @Override
    public Set<String> getTopics() throws NotLoggedInException {
        if (this.isLoggedIn()) {
            return this.allMessages.keySet();
        }
        throw new NotLoggedInException();
    }

    @Override
    public List<Message> getMessages(String topic) {
        return this.allMessages.get(topic);
    }

    @Override
    public final File getPath() {
        return config.getMessagePath();
    }

    @Override
    public void newMessage(String topic, String text) throws IOException,
            FileCorruptedException, NotLoggedInException {
        if (this.user != null) {
            if (topic.length() > 0 && text.length() > 0) {
                this.user.newMessage(topic, text);
                this.io.save(user);
                this.loadAllUserMessages();
            } else {
                // túl rövid téma vagy szöveg
            }
        } else {
            throw new NotLoggedInException();
        }
    }

    @Override
    public ArrayList<String> getAllUsers() {
        return io.usersList();
    }

    @Override
    public long getUserFileSize(String username) throws IOException {
        return io.getUserFile(username).length();
    }

    @Override
    public void loadAllUserMessages() throws IOException, FileCorruptedException {

        Map<String, List<Message>> messages = new HashMap<String, List<Message>>();
        ArrayList<String> users = this.io.usersList();
        
        if (users != null) {
            for (String username : users) {
                User u = this.io.load(username);
                if (u.getUsername().equals(username)) {
                    if (u.getMessages() != null) {
                        for (Message m : u.getMessages()) {
                            if (messages.containsKey(m.getTopic())) {
                                messages.get(m.getTopic()).add(m);
                            } else {
                                ArrayList<Message> newTopicMessages = new ArrayList<Message>();
                                newTopicMessages.add(m);
                                messages.put(m.getTopic(), newTopicMessages);
                            }
                        }
                    } else {
                        // nincsenek uzenetei
                    }
                } else {
                    // nem a fajlnevnek megfelelo username
                }
            }

        } else {
            // nincsenek felhasznalok
        }
        for (List<Message> m : messages.values()) {
            Collections.sort(m);
        }
        this.allMessages = messages;
    }

    @Override
    public Config getConfig() {
        return this.config;
    }

    
    // Belső FileIO osztály 
    private class FileIO {

        public FileIO() throws IOException, ConfigFileNotFoundException, FileCorruptedException {
            
            // A forrásfile elérésének utvonala, védettségi szinttel
            String classPath = FileIO.class.getProtectionDomain().getCodeSource().getLocation().getPath();
            // config file értelmezése és meghatérozása
            String confPath = URLDecoder.decode(classPath, "UTF-8");

            // ha .jar-ra végződik akkor megadja a megfelelő könyvtárat
            // ha nem akkor egy üres útvonalat állít be 
            if (confPath.endsWith(".jar")) {
                confPath = confPath.substring(0, confPath.lastIndexOf(File.separatorChar)) + File.separator;
            } else {
                confPath = "";
            }
            // A végére illeszti a file nevét, a megfelelő könyvtárban
            confPath += "DBMconfig";
            try {
                config = new Config(new File(confPath));
            } catch (ClassNotFoundException ex) {
                throw new FileCorruptedException();
            }
        }

        // File elérési útvonal megszerzése
        private File getUserFile(String username) throws IOException {

            System.out.println(config.getMessagePath().getCanonicalPath() + File.separator + username);
            File userFile = new File(config.getMessagePath().getCanonicalPath() + File.separator + username);
            return userFile;

        }

        // File létezik-e
        public boolean userFileExists(String username) throws IOException {
            return (this.getUserFile(username).exists());
        }

        // Űzenetek lementése a file-ba
        public void save(User user) throws IOException {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(this.getUserFile(user.getUsername())));
            out.writeObject(user);
            out.close();
        }

        // Üzenetek kiolvasása egy megadott file-ból
        public User load(String username) throws IOException, FileCorruptedException {
            
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(this.getUserFile(username)));

            User user;
            try {
                user = (User) in.readObject();
            } catch (ClassNotFoundException e) {
                throw new FileCorruptedException();
            }
            in.close();
            return user;
        }

        // Létező felhasználók listázása
        public ArrayList<String> usersList() {
            ArrayList<String> users = new ArrayList<String>();
            File[] all = config.getMessagePath().listFiles();
            if (all != null) {
                for (File f : all) {
                    if (f.exists() && f.isFile() && !f.isHidden()) {
                        users.add(f.getName());
                    }
                }
            }
            return users;
        }
    }
}
